﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.ComponentModel;
using System.Data;                                                   
using System.Drawing;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.Text;
using System.IO;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Xml.Serialization;

using Microsoft.SqlServer.Types;
using System.Data.Sql;
using System.Data.SqlClient;

using System.Drawing.Drawing2D;

namespace MapViewer
{
    public partial class MapViewer_Control : System.Web.UI.UserControl
    {
        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        // Interface Attributes; Bindable and available as control properites 
        // in VS
        //---------------------------------------------------------------------
        //---------------------------------------------------------------------

        // the output size of the map image in pixels
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("1200,1200")]
        public Size DisplaySize
        {
            get { return layermanager.outputsize; }
            set
            {
                // set values into layer manager
                layermanager.outputsize = value;

                // set our own values
                width = value.Width;
                height = value.Height;
            }
        }

        // the viewport size of the map image in pixels
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("400,400")]
        public Size ViewPortSize
        { get; set; }

        // set the envelope boundary. This is the factor by which the selected item's 
        // envelope is expanded to show it in context.
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("")]
        public float EnvelopeBoundary
        {
            get { return layermanager.EnvelopeBoundary; }
            set { layermanager.EnvelopeBoundary = value; }
        }

        // set the smallest envelope size to display. The units are the units within
        // the layer
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("")]
        public float minimumEnvelopSize
        {
            get { return layermanager.minimumEnvelopeSize; }
            set { layermanager.minimumEnvelopeSize = value; }
        }

        // identify the "selected" layer - this is the layer we'll query for key values
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("")]
        [Localizable(true)]
        public String SelectionLayerName
        {
            get { return layermanager.SelectionLayerName; }
            set { layermanager.SelectionLayerName = value; }
        }

        // Whether or not to show "clickable" map - e.g. should we generate
        // the html <area> control
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("")]
        public bool GenerateClickMap
        {
            get { return Session.Get_Boolean("GenerateClickMap"); }
            set { Session.Set_Boolean("GenerateClickMap", value); }
        }

        // URL to go to when a region on the map is clicked
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("")]
        [Localizable(true)]
        public string MapUrl
        {
            get { return Session.Get_String("MapUrl"); }
            set { Session.Set_String("MapUrl", value); }
        }

        // value to pass as 'key', added to the MapUrl link
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("")]
        [Localizable(true)]
        public String MapKey
        {
            get { return Session.Get_String("MapKey"); }
            set { Session.Set_String("MapKey", value); }
        }

        // value to pass as 'field', added to the MapUrl link
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("")]
        [Localizable(true)]
        public String MapField
        {
            get { return Session.Get_String("MapField"); }
            set { Session.Set_String("MapField", value); }
        }

        // unit displayed in scale text in corner of control
        [Bindable(true)]
        [Category("Setup")]
        [DefaultValue("")]
        [Localizable(true)]
        public String MapUnits
        {
            get { return Session.Get_String("MapUnits"); }
            set { Session.Set_String("MapUnits", value); }
        }

        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        // Public Properties and Methods
        //---------------------------------------------------------------------
        //---------------------------------------------------------------------

        // list of values that we'll use to select data from the 'selected' layer
        internal LayerValueList _SelectionValues = new LayerValueList();
        public LayerValueList SelectionValues
        {
            get { return _SelectionValues; }
            set { _SelectionValues = value; }
        }
        
        // Add a layer to the layer manager
        public void Add(String name, LayerInfo layer)
        {
            layermanager.Add(name, layer);
        }

        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        // Internal Data Elements and Attributes
        //---------------------------------------------------------------------
        //---------------------------------------------------------------------

        // drawing object
        internal Drawing drawing = new Drawing();

        // collection of all the layers that we're going to draw
        internal LayerManager layermanager = new LayerManager();

        // internal diagnostic - count the time to do all our work
        private DateTime start;
        private DateTime stop;

        // flag - have we been initialized?
        private Boolean initialized = false;

        //---------------------------------------------------------------------
        // saved coordinates for panning and zooming 
        //---------------------------------------------------------------------
        private float top
        {
            get { return geo_ul_y.Get_Float(); }
            set { geo_ul_y.Set_Float(value); }
        }

        private float left
        {
            get { return geo_ul_x.Get_Float(); }
            set { geo_ul_x.Set_Float(value); }
        }

        private float bottom
        {
            get { return geo_br_y.Get_Float(); }
            set { geo_br_y.Set_Float(value); }
        }

        private float right
        {
            get { return geo_br_x.Get_Float(); }
            set { geo_br_x.Set_Float(value); }
        }

        //---------------------------------------------------------------------
        // save height and width of the image to draw
        //---------------------------------------------------------------------
        private float width
        {
            get { return Session.Get_Float("width"); }
            set { Session.Set_Float("width", value); }
        }

        private float height
        {
            get { return Session.Get_Float("height"); }
            set { Session.Set_Float("height", value); }
        }

        //---------------------------------------------------------------------
        // Page Load - Setup the control for use
        //---------------------------------------------------------------------
        protected void Page_Load(object sender, EventArgs e)
        {
            // Make sure we're initialized
            if (!initialized)
                Initialize();
        }

        //---------------------------------------------------------------------
        // Draw beginning with a key value
        //---------------------------------------------------------------------
        public void Initialize()
        {
            // check for things that must be defined for this to work
            if (GenerateClickMap)
            {
                if (String.IsNullOrEmpty(MapUrl))
                    throw new Exception("MapUrl must be defined for click-map processing");
                if (String.IsNullOrEmpty(MapKey))
                    MapKey = "key";
                if (String.IsNullOrEmpty(MapField))
                    MapField = "field";
            }

            // recover saved layer manager 
            if (null != Session["layermanager"])
            {
                layermanager = (LayerManager)Session["layermanager"];
            }

            if (0 == layermanager.Count)
                throw new Exception("At least one layer must be defined");

            // set default sizes
            if (0 == width || 0 == height)
            {
                DisplaySize = new Size(600, 600);
                ViewPortSize = new Size(300, 300);
            }

            // update variable scripts, replacing values
            image_x.Text = ((int)width).ToString();
            image_y.Text = ((int)height).ToString();
            viewport_x.Text = ((int)ViewPortSize.Width).ToString();
            viewport_y.Text = ((int)ViewPortSize.Height).ToString();

            // set viewport size and image location
            imageparent.Style.Add("width", ((int)ViewPortSize.Width).ToString() + "px");
            imageparent.Style.Add("height", ((int)ViewPortSize.Height).ToString() + "px");
            dragme.Style.Add("left", ((int)(-(width / 2 - ViewPortSize.Width / 2))).ToString() + "px");
            dragme.Style.Add("top", ((int)(-(height / 2 - ViewPortSize.Height / 2))).ToString() + "px");
            dragme.Style.Add("position", "relative");

            // send values to drawing object
            drawing.GenerateClickMap = GenerateClickMap;
            drawing.MapField = MapField;
            drawing.MapKey = MapKey;
            drawing.MapUrl = MapUrl;
            drawing.width = width;
            drawing.height = height;

            // screwball work-around because .NET can't tell the difference between
            // server side and client side events
            map.Attributes.Add("onload", "javascript:image_load(); return false;");

            // set "initialized" flag
            initialized = true;
        }

        //---------------------------------------------------------------------
        // Draw beginning with a key value
        //---------------------------------------------------------------------
        public void DrawMapByKey()
        {
            // diagnostic - start counting time
            start = DateTime.Now;

            // Make sure we're initialized
            if (!initialized)
                Initialize();

            // get region for entered key value 
            layermanager.GetSelectionRegion(SelectionValues);

            // draw the region based on our newly selected envelope
            drawing.DrawAllLayers(layermanager);

            // save the layer definitions
            Session["layermanager"] = layermanager;

            // save the drawing results to HTML
            Set_Values();

            // diagnostic - print the count of paths
            pathcounter.Text = drawing.pathcount.ToString();
            geocounter.Text = drawing.geocount.ToString();

            // diagnostic - stop counting time and list processing time in milliseconds
            stop = DateTime.Now;
            TimeSpan d = stop - start;
            duration.Text = d.ToString();
        }

        //---------------------------------------------------------------------
        // Handle zoom in/out processing
        //---------------------------------------------------------------------
        public void Zoom(float shiftleftright, float shiftupdown)
        {
            // diagnostic - start counting time
            start = DateTime.Now;

            // Make sure we're initialized
            if (!initialized)
                Initialize();

            // throw an error if the MapUrl is not set
            if (String.IsNullOrEmpty(MapUrl))
                throw new Exception("Must Specify a URL for the map areas.");

            // calculate new region coords based on shift values
            top -= shiftupdown;
            bottom += shiftupdown;
            left -= shiftleftright;
            right += shiftleftright;

            // do drawing for a zoom operation
            RedrawPanAndZoom();
        }

        //---------------------------------------------------------------------
        // handle pan left/right/up/down processing
        //---------------------------------------------------------------------
        public void Pan(float shiftleftright, float shiftupdown)
        {
            // diagnostic - start counting time
            start = DateTime.Now;

            // Make sure we're initialized
            if (!initialized)
                Initialize();

            // throw an error if the MapUrl is not set
            if (String.IsNullOrEmpty(MapUrl))
                throw new Exception("Must Specify a URL for the map areas.");

            // calculate new region coords based on shift values
            top += shiftupdown;
            bottom += shiftupdown;
            left += shiftleftright;
            right += shiftleftright;

            // do drawing for a pan operation
            RedrawPanAndZoom();
        }

        //---------------------------------------------------------------------
        // Redraw based on a region rather than a key value
        //---------------------------------------------------------------------
        private void RedrawPanAndZoom()
        {
            // create polygon text - manually create our new envelope
            StringBuilder poly = new StringBuilder();
            poly.Append("POLYGON((");
            poly.Append(left.ToString()).Append(" ").Append(top.ToString()).Append(", ");
            poly.Append(right.ToString()).Append(" ").Append(top.ToString()).Append(", ");
            poly.Append(right.ToString()).Append(" ").Append(bottom.ToString()).Append(", ");
            poly.Append(left.ToString()).Append(" ").Append(bottom.ToString()).Append(", ");
            poly.Append(left.ToString()).Append(" ").Append(top.ToString()).Append(" ");
            poly.Append("))");

            // set the envelope into the layer manager
            layermanager.EnvelopeFromText(poly.ToString());

            // get data and draw based on our new region
            drawing.DrawAllLayers(layermanager);

            // save drawing results to HTML
            Set_Values();

            // diagnostic - print the count of paths
            pathcounter.Text = drawing.pathcount.ToString();
            geocounter.Text = drawing.geocount.ToString();

            // diagnostic - stop counting time and list processing time in milliseconds
            stop = DateTime.Now;
            TimeSpan d = stop - start;
            duration.Text = d.ToString();
        }

        //---------------------------------------------------------------------
        // After drawing is done, set resulting values into the HTML template
        //---------------------------------------------------------------------
        private void Set_Values()
        {
            // set the scale display
            scaletext.Text = (1 / drawing.scale).ToString("0.00") + " " + MapUnits;

            // set the image name into the html so the browser knows which image to get
            map.Attributes.Add("src", "MapViewer_GetMap.aspx?imagename=" + drawing.imagename.ToString());
            
            // set the clickable map image
            parcelmap.InnerHtml = drawing.areamap.ToString();
            
            // save our coordinates for panning and zooming - if we have anything to save.
            if (!layermanager.Envelope.IsNull)
            {
                top = (float)layermanager.Envelope.STPointN(1).STY;
                left = (float)layermanager.Envelope.STPointN(1).STX;
                bottom = (float)layermanager.Envelope.STPointN(3).STY;
                right = (float)layermanager.Envelope.STPointN(3).STX;
            }
        }


        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        // Image click - handle image callback from click map
        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        public void Image_Click()
        {
            SelectionLayerName = Request["layer"];
            String searchvalue = Request["key"];
            String searchfield = Request["field"];

            // if we actually have something ...
            if (!String.IsNullOrEmpty(searchvalue) 
                && !String.IsNullOrEmpty(searchfield)
                && !String.IsNullOrEmpty(SelectionLayerName))
            {
                // set values
                SelectionValues.Add(new Parameter(searchfield, DbType.String, searchvalue));

                // go draw our map
                DrawMapByKey();
            }
        }

        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        // Zoom button clicks
        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        
        protected void ImageButtonIn_Click(object sender, EventArgs e)
        {
            float leftright = (left - right) / 6;
            float updown = (top - bottom) / 6;

            Zoom(leftright, updown);
        }

        protected void ImageButtonOut_Click(object sender, EventArgs e)
        {
            float leftright = (left - right) / 4;
            float updown = (top - bottom) / 4;

            Zoom(-leftright, -updown);
        }

        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        // Mouse Panning - non ajax
        //---------------------------------------------------------------------
        //---------------------------------------------------------------------

        protected void HiddenButton_Click(object sender, EventArgs e)
        {
            float x = (float)Convert.ToDouble(deltaX.Text);
            float y = (float)Convert.ToDouble(deltaY.Text);
            int size_x = Convert.ToInt32(DisplaySize.Width);
            int size_y = Convert.ToInt32(DisplaySize.Height);

            float leftright = (x/size_x)*(left - right);
            float updown = (y/size_y)*(top - bottom);

            Pan(leftright, -updown);
        }
    }
}